// Copyright 2024 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sql

import (
	ast "github.com/dolthub/vitess/go/vt/sqlparser"
)

// AuthorizationQueryState contains any state that should be retained for an entire query, so that it is loaded once and
// then reused. If an error was created in NewQueryState, then that error should be retained here and returned in
// HandleAuth.
type AuthorizationQueryState interface {
	// Error returns the error contained within the state if one exists, otherwise returns nil.
	Error() error
	// AuthorizationQueryStateImpl has no actual usage besides enforcing that the returned object adheres to this
	// interface.
	AuthorizationQueryStateImpl()
}

// AuthorizationHandlerFactory creates an AuthorizationHandler, which will be used for all authorization needs.
type AuthorizationHandlerFactory interface {
	// CreateHandler creates an AuthorizationHandler from the given catalog.
	CreateHandler(cat Catalog) AuthorizationHandler
}

// AuthorizationHandler handles the authorization of queries, generally through the use of a privilege system. This
// handler exists to create handlers that will operate on a single query.
type AuthorizationHandler interface {
	// NewQueryState returns some kind of state that should be retained for an entire query. This is for the purposes of
	// optimization. If nothing needs to be retained between calls for a single query, then it is valid for this to
	// return nil. If an error occurs, then the state should contain the error so that it may be returned in HandleAuth.
	NewQueryState(ctx *Context) AuthorizationQueryState
	// HandleAuth checks whether the authentication information is valid. The state may be nil, therefore this function
	// may need to internally construct the state if a nil one is provided.
	HandleAuth(ctx *Context, state AuthorizationQueryState, auth ast.AuthInformation) error
	// HandleAuthNode handles the authentication of nodes that implement AuthorizationCheckerNode. These are often used
	// by integrators that do not modify the AST, and instead prefer a node-based form of authentication.
	HandleAuthNode(ctx *Context, state AuthorizationQueryState, node AuthorizationCheckerNode) error
	// CheckDatabase returns nil when access to the given database is permitted. If the database name is empty, it
	// should be assumed that the context's current database is being checked.
	CheckDatabase(ctx *Context, state AuthorizationQueryState, dbName string) error
	// CheckSchema returns nil when access to the given schema is permitted. If the database or schema name is empty,
	// it should be assumed that the context's current database and schema are being checked.
	CheckSchema(ctx *Context, state AuthorizationQueryState, dbName string, schemaName string) error
	// CheckTable returns nil when access to the given table is permitted. If the database or schema name is empty,
	// it should be assumed that the context's current database and schema are being checked. Should return an error if
	// the table name is empty.
	CheckTable(ctx *Context, state AuthorizationQueryState, dbName string, schemaName string, tableName string) error
}

// AuthorizationCheckerNode is a node that implements its own authorization checking.
type AuthorizationCheckerNode interface {
	Node
	// CheckAuth performs any authorization needed for the node, returning true if authorization succeeded.
	CheckAuth(ctx *Context, checker PrivilegedOperationChecker) bool
}

// globalAuthorizationHandlerFactory is the factory that is used when creating a sql.Catalog. This should never be
// fetched directly, instead it should be fetched using GetAuthorizationHandlerFactory.
var globalAuthorizationHandlerFactory AuthorizationHandlerFactory

// SetAuthorizationHandlerFactory sets the desired authorization factory. Defaults to the factory that expects an AST
// that was generated by Vitess. This factory is used when creating a sql.Catalog, so it must be changed before the
// creation of a catalog. If set to nil, then all auth-related queries will succeed.
func SetAuthorizationHandlerFactory(factory AuthorizationHandlerFactory) {
	globalAuthorizationHandlerFactory = factory
}

// GetAuthorizationHandlerFactory returns the global AuthorizationHandlerFactory that was set using
// SetAuthorizationHandlerFactory.
func GetAuthorizationHandlerFactory() AuthorizationHandlerFactory {
	if globalAuthorizationHandlerFactory != nil {
		return globalAuthorizationHandlerFactory
	}
	return NoopAuthorizationHandlerFactory{}
}
